package com.ym.ms.config;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.api.R;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.Enumeration;
import java.util.Set;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * @author zhang
 */
@ControllerAdvice
@ResponseBody
public class CommonExceptionAdvice {

    private static Logger logger = LoggerFactory.getLogger(CommonExceptionAdvice.class);


    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public R handleMissingServletRequestParameterException(MissingServletRequestParameterException e, HttpServletRequest req) {
        logger.error("缺少请求参数", e.getMessage(), ExceptionUtils.getStackTrace(e));
        logger.error("异常：请求参数-" + getReqParams(req).toJSONString());
        return R.failed(e.getMessage());
    }


    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public R handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        logger.error("参数解析失败", e.getMessage(), e);
        return R.failed("could_not_read_json");
    }


    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
//        String field = error.getField();
        String code = error.getDefaultMessage();
        String message = String.format("%s", code);
        return R.failed(message);
    }


    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BindException.class)
    public R handleBindException(BindException e) {
        logger.error("参数绑定失败", e.getMessage(), ExceptionUtils.getStackTrace(e));
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        String field = error.getField();
        String code = error.getDefaultMessage();
        String message = String.format("%s:%s", field, code);
        return R.failed(message);
    }


    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ConstraintViolationException.class)
    public R handleServiceException(ConstraintViolationException e) {
        logger.error("参数验证失败", e.getMessage(), ExceptionUtils.getStackTrace(e));
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        ConstraintViolation<?> violation = violations.iterator().next();
        String message = violation.getMessage();
        return R.failed("parameter:" + message);
    }


    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ValidationException.class)
    public R handleValidationException(ValidationException e) {
        logger.error("参数验证失败", e);
        return R.failed("validation_exception");
    }

//    /**
//     * 405 - Method Not Allowed
//     */
//    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
//    @ExceptionHandler(NoSuchMethodException.class)
//    public Result handleHttpRequestMethodNotSupportedException(NoSuchMethodException e,HttpServletRequest req) {
//        logger.error("不支持当前请求方法", e);
//        return R.failed()("request_method_not_supported");
//    }

    /**
     * 415 - Unsupported Media Type
     */
//    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
//    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
//    public Result handleHttpMediaTypeNotSupportedException(Exception e) {
//        logger.error("不支持当前媒体类型", e);
//        return R.failed()("content_type_not_supported");
//    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(Exception.class)
    public R handleException(Exception e) {
        String errMsg = "";
    if (e instanceof NoHandlerFoundException) {
            errMsg = "该接口不存在";
        } else if (e instanceof ServletException) {
            errMsg = "该接口不存在";
        } else if (e instanceof HttpMessageNotReadableException) {
            errMsg = "JSON数据转换异常！";
//        } else if (e instanceof MethodArgumentNotValidException) {
//            MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
//            ex.getBindingResult().get
//            result.setCode(ResultCode.FAIL).setMsg("参数校验错误:"+ BeanValidators.extractPropertyAndMessageAsString(e.getConstraintViolations());
        } else {
            errMsg = e.getMessage();
        }
        logger.error("通用异常" + errMsg, e.getMessage(), e);
        return R.failed("通用异常：" + errMsg);
    }

    /**
     * 操作数据库出现异常:名称重复，外键关联
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(DataIntegrityViolationException.class)
    public R handleException(DataIntegrityViolationException e) {
        logger.error("操作数据库出现异常:", e);
        return R.failed("操作数据库出现异常：字段重复, 有外键关联, 字段长度超出范围等");
    }

    private JSONObject getReqParams(HttpServletRequest request) {
        checkNotNull(request, "request");
        JSONObject reb = new JSONObject();
        Enumeration<String> enu = request.getParameterNames();
        while (enu.hasMoreElements()) {
            String paraName = (String) enu.nextElement();
            reb.put(paraName, request.getParameter(paraName));
        }
        return reb;
    }
}